#include "userprog/syscall.h"
#include <stdio.h>
#include <syscall-nr.h>
#include "threads/interrupt.h"
#include "threads/thread.h"
#include "devices/shutdown.h" /* Imports shutdown_power_off() for use in halt(). */
#include "threads/synch.h"//用锁

struct lock file_lock; //文件锁

void get_stack_arguments (struct intr_frame *f, int *args, int num_of_args);//声明工具函数

static void syscall_handler (struct intr_frame *);

void
syscall_init (void) 
{
  lock_init(&file_lock);//lock init

  intr_register_int (0x30, 3, INTR_ON, syscall_handler, "syscall");
}

static void
syscall_handler (struct intr_frame *f) 
{
  int syscall_number = *(int *)f->esp;
  int args[3];//储存stack中参数
  void * phys_page_ptr;//储存物理页地址
  switch (syscall_number) 
    {
      case SYS_HALT:
        {
          sys_halt();
          break;
        }
      case SYS_EXIT:
        {
          get_stack_arguments(f, &args[0], 1);//获取exit status
          sys_exit(args[0]);
          break;
        }
      case SYS_EXEC:
        {
          /* The first argument of exec is the entire command line text for executing the program */
          get_stack_arguments(f, &args[0], 1);

          /* Ensures that converted address is valid. */
          phys_page_ptr = (void *) pagedir_get_page(thread_current()->pagedir, (const void *) (args[0]));
          if (phys_page_ptr == NULL)
          {
            sys_exit(-1);
          }
          args[0] = (int) phys_page_ptr;

          /* Return the result of the sys_exec() function in the eax register. */
          f->eax = sys_exec((const char *) args[0]);
          break;
        }

        case SYS_WRITE:
        {
        /* Get three arguments off of the stack. The first represents the fd, the second
           represents the buffer, and the third represents the buffer length. */
        get_stack_arguments(f, &args[0], 3);

        /* Ensures that converted address is valid. */
        phys_page_ptr = pagedir_get_page(thread_current()->pagedir, (const void *) args[1]);
        if (phys_page_ptr == NULL)
        {
          sys_exit(-1);
        }
        args[1] = (int) phys_page_ptr;

        /* Return the result of the write() function in the eax register. */
        f->eax = sys_write(args[0], (const void *) args[1], (unsigned) args[2]);
        break;
      }

		default:
        printf("[ERROR] system call %d is unimplemented!\n", syscall_number);
        sys_exit(-1);
        break;
    }
}

/****************** System Call Implementations ********************/
void 
sys_halt(void) 
{
  shutdown_power_off();
}

/* 终止当前用户程序。其退出状态会被打印，并将状态返回给内核。 */
void 
sys_exit(int status) 
{
  thread_current()->exit_status = status;
	printf("%s: exit(%d)\n", thread_current()->name, status);
  thread_exit ();
}

// 执行程序系统调用
pid_t sys_exec(const char *cmdline) {
  if(!cmdline)
	{
		return -1;
	}
  lock_acquire(&file_lock);
  /* Get and return the PID of the process that is created. */
	pid_t child_tid = process_execute(cmdline);
  lock_release(&file_lock);
	return child_tid;
}

/* Writes LENGTH bytes from BUFFER to the open file FD. Returns the number of bytes actually written,
 which may be less than LENGTH if some bytes could not be written. */
 int sys_write (int fd, const void *buffer, unsigned length)
 {
   /* list element to iterate the list of file descriptors. */
   struct list_elem *temp;
 
   lock_acquire(&file_lock);
 
   /* If fd is equal to one, then we write to STDOUT (the console, usually). */
   if(fd == 1)
   {
     putbuf(buffer, length);
     lock_release(&file_lock);
     return length;
   }
   /* If the user passes STDIN or no files are present, then return 0. */
   if (fd == 0 || list_empty(&thread_current()->file_descriptors))
   {
     lock_release(&file_lock);
     return 0;
   }
 
   /* Check to see if the given fd is open and owned by the current process. If so, return
      the number of bytes that were written to the file. */
   for (temp = list_front(&thread_current()->file_descriptors); temp != NULL; temp = temp->next)
   {
       struct thread_file *t = list_entry (temp, struct thread_file, file_elem);
       if (t->file_descriptor == fd)
       {
         int bytes_written = (int) file_write(t->file_addr, buffer, length);
         lock_release(&file_lock);
         return bytes_written;
       }
   }
 
   lock_release(&file_lock);
 
   /* If we can't write to the file, return 0. */
   return 0;
 }

//获取stack中的参数
void get_stack_arguments (struct intr_frame *f, int *args, int num_of_args)
{
  int i;
  int *ptr;
  for (i = 0; i < num_of_args; i++)
    {
      ptr = (int *) f->esp + i + 1;
      args[i] = *ptr;
    }
}